// 5-3 多线程处理-连接和分离线程
/**
 * C++ 多线程
 * 多线程是多任务处理的一种特殊形式，多任务处理允许让电脑同时运行两个或两个以上的程序。一般情况下，两种类型的多任务处理：基于进程和基于线程。

      **基于进程**的多任务处理是**程序的并发执行**。
      **基于线程****的多任务处理是**同一程序的片段的并发执行**。

 * 多线程程序包含可以同时运行的两个或多个部分。这样的程序中的每个部分称为一个线程，每个线程定义了一个单独的执行路径。

 * 本教程假设您使用的是 Linux 操作系统，我们要使用 POSIX 编写多线程 C++ 程序。POSIX Threads 或 Pthreads 提供的 API 可在多种类 Unix POSIX 系统上可用，比如 FreeBSD、NetBSD、GNU/Linux、Mac OS X 和 Solaris。

 *
 *
 * 创建线程
 * 下面的程序，我们可以用它来创建一个 POSIX 线程：

        #include <pthread.h>
        pthread_create (thread, attr, start_routine, arg) 

 * 在这里，pthread_create 创建一个新的线程，并让它可执行。下面是关于参数的说明：

      参数	        描述
      thread	      指向线程标识符指针。
      attr	        一个不透明的属性对象，可以被用来设置线程属性。您可以指定线程属性对象，也可以使用默认值 NULL。
      start_routine	线程运行函数起始地址，一旦线程被创建就会执行。
      arg	          运行函数的参数。它必须通过把**引用作为指针强制转换为 void 类型**进行传递。如果没有传递参数，则使用 NULL。
 
 * 创建线程成功时，函数返回 0，若返回值不为 0 则说明创建线程失败。
 *
 * 
 * 终止线程
 * 使用下面的程序，我们可以用它来终止一个 POSIX 线程：

        #include <pthread.h>
        pthread_exit (status) 

 * 在这里，pthread_exit 用于显式地退出一个线程。通常情况下，pthread_exit() 函数是在线程完成工作后无需继续存在时被调用。

 * 如果 main() 是在它所创建的线程之前结束，并通过 pthread_exit() 退出，那么其他线程将继续执行。否则，它们将在 main() 结束时自动被终止。
 *
 * --------------------------------
 * 连接和分离线程
 * 我们可以使用以下两个函数来连接或分离线程：

        pthread_join (threadid, status) 
        pthread_detach (threadid) 

 * pthread_join() 子程序阻碍调用程序，直到指定的 threadid 线程终止为止。当创建一个线程时，它的某个属性会定义它是否是可连接的（joinable）或可分离的（detached）。**只有创建时定义**为可连接的线程才可以被连接。如果线程创建时被定义为可分离的，则它永远也不能被连接。

 * 这个实例（#1 实例一）演示了如何使用 pthread_join() 函数来等待线程的完成
*/

#include <iostream>
#define __mark 1

/**
 * #1 实例1
 * 编译 g++ 5_3.cpp -lpthread -o ./build/5_1.o
 * 执行程序 5_3.o
*/
#if 1 == __mark
#include <pthread.h>
#include <windows.h>
// using namespace std;

void *wait(void *tid)
{
  Sleep(1);
  std::cout << "sleep in thread" << std::endl;
  std::cout << "thread " << *((int *)tid) << " exiting" << std::endl;
  pthread_exit(NULL);
  return 0;
}

int main(void)
{
  const int size = 5;
  pthread_t threads[size];
  pthread_attr_t attr;
  void *status;

  // 初始化属性，并设置线程为可连接的
  pthread_attr_init(&attr); // int pthread_attr_init(pthread_attr_t *attr)
  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

  for (int i = 0; i < size; i++)
  {
    std::cout << "main() create thread " << i << " start ..." << std::endl;
    // int res = pthread_create(&threads[i], &attr, wait, &i);
    int res = pthread_create(&threads[i], NULL, wait, &i); // 看不懂为啥不用
    if (res != 0)
    {
      std::cerr << "create thread " << i << " error" << std::endl;
      exit(-1);
    }
    // std::cout << "main() create thread " << i << " success !" << std::endl;
  }

  // 删除属性 并等待其他线程
  pthread_attr_destroy(&attr); // 看不懂这步，难道pthread_attr_setdetachstate 里面设置的是 pthread_create方法，删了防止之后在调pthread_create还是那配置？不然，pthread_join可调性是里面的默认值？那还设置干啥？
  for (int i = 0; i < size; i++)
  {
    int rv = pthread_join(threads[i], &status);
    // int rv = pthread_detach(threads[i]);
    if (rv != 0)
    {
      std::cerr << "Error:thread " << i << " unable to join" << std::endl;
      exit(-1);
    }
    std::cout << "Main: completed thread id :" << i << std::endl;
    std::cout << "thread " << i << " exiting with status :" << status << std::endl;
  }

  std::cout << "Main: program exiting..." << std::endl;
  pthread_exit(NULL);

  printf("---------------------end---------------\n");
  return 0;
}
/**
 * #2 实例二
*/
#elif 2 == __mark
int main(void)
{

  printf("---------------------end---------------\n");
  return 0;
}
#endif
